package cn.edu.sgu.www.pms.config;

import cn.edu.sgu.www.pms.entity.Permission;
import cn.edu.sgu.www.pms.mapper.ApplicationMapper;
import cn.edu.sgu.www.pms.mapper.PermissionMapper;
import cn.edu.sgu.www.pms.shiro.UserPasswordMatcher;
import cn.edu.sgu.www.pms.shiro.UsernameRealm;
import cn.edu.sgu.www.pms.shiro.filter.PermsFilter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Apache Shiro配置类
 * @author 沐雨橙风ιε
 * @version 1.0
 */
@Configuration
public class ShiroConfig {

    @Value("${spring.application.name}")
    private String service;

    private final PermissionMapper permissionMapper;
    private final ApplicationMapper applicationMapper;

    @Autowired
    public ShiroConfig(PermissionMapper permissionMapper, ApplicationMapper applicationMapper) {
        this.permissionMapper = permissionMapper;
        this.applicationMapper = applicationMapper;
    }

    @Bean
    public UsernameRealm usernameRealm(UserPasswordMatcher passwordMatcher) {
        UsernameRealm usernameRealm = new UsernameRealm();

        // 设置密码匹配器
        usernameRealm.setCredentialsMatcher(passwordMatcher);

        return usernameRealm;
    }

    /**
     * 配置Shiro过滤器工厂
     * @param securityManager 安全管理器
     * @return ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 注册安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        /*
         * 设置登录页面的地址
         * 当用户访问认证资源的时候，如果用户没有登录，就会跳转到指定的页面
         */
        shiroFilterFactoryBean.setLoginUrl("/login.html");

        /*
         * 设置访问未授权资源时跳转的页面地址
         * 当用户访问认证资源的时候，如果用户没有改 资源所需的访问权限，就会跳转到指定的页面
         */
        // shiroFilterFactoryBean.setUnauthorizedUrl("/user/unauthorized");

        // 定义资源访问规则
        Map<String, String> filterMap = new LinkedHashMap<>();

        /*
         * 过滤器说明
         * anon：不需要认证就可以访问的资源
         * authc：需要登录认证才能访问的资源
         */
        // 定义需要登录认证才能访问的资源
        filterMap.put("/", "authc");
        filterMap.put("/index.html", "authc");
        filterMap.put("/html/*.html", "authc");
        // knife4j
        filterMap.put("/doc.html", "authc");
        filterMap.put("/v2/api-docs", "authc");
        filterMap.put("/swagger-resources", "authc");

        // 定义不需要认证就能访问的资源
        filterMap.put("/login.html", "anon");

        // 查询本项目的RPC接口资源
        Integer serviceId = applicationMapper.getServiceId(service);

        QueryWrapper<Permission> queryWrapper = new QueryWrapper<>();

        queryWrapper.select("url");
        queryWrapper.eq("remote", 1);
        queryWrapper.eq("service_id", serviceId);

        List<Permission> list = permissionMapper.selectList(queryWrapper);

        List<String> remoteUrls = list.stream().map(new Function<Permission, String>() {
            @Override
            public String apply(Permission permission) {
                return permission.getUrl();
            }
        }).collect(Collectors.toList());

        // 添加自定义过滤器
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();

        filters.put("perms", new PermsFilter(remoteUrls));

        shiroFilterFactoryBean.setFilters(filters);

        // 定义资源和访问权限的关系
        List<Permission> permissions = permissionMapper.selectPermissions(serviceId);

        for (Permission permission : permissions) {
            filterMap.put(permission.getUrl(), "perms[" + permission.getValue() + "]");
        }

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);

        return shiroFilterFactoryBean;
    }

}